#ifndef _NUMERIC_H_
#define _NUMERIC_H_

#include "iterator.hpp"

namespace mystl {
template <typename ForwardIterator, typename T>
inline void iota(ForwardIterator first, ForwardIterator last, T value) {
    for (; first != last; ++first) {
        *first = value;
        ++value;
    }
}

template <typename InputIterator, typename T>
inline T accumulate(InputIterator first, InputIterator last, T initValue) {
    for (; first != last; ++first) {
        initValue = initValue + *first;
    }

    return initValue;
}

template <typename InputIterator, typename T, typename BinaryFunction>
inline T accumulate(InputIterator first, InputIterator last, T initValue, BinaryFunction binaryFunction) {
    for (; first != last; ++first) {
        initValue = binaryFunction(initValue, *first);
    }

    return initValue;
}

template <typename InputIteratorL, typename InputIteratorR, typename T>
inline T inner_product(InputIteratorL firstL, InputIteratorL lastL, InputIteratorR firstR, T initValue) {
    for (; firstL != lastL; ++firstL, ++firstR) {
        initValue = initValue + (*firstL) * (*firstR);
    }

    return initValue;
}

template <typename InputIteratorL, typename InputIteratorR, typename T, typename BinaryFunctionL, typename BinaryFunctionR>
inline T inner_product(InputIteratorL firstL, InputIteratorL lastL, InputIteratorR firstR, T initValue,
                       BinaryFunctionL binaryFunctionL, BinaryFunctionR binaryFunctionR) {
    for (; firstL != lastL; ++firstL, ++firstR) {
        initValue = binaryFunctionL(initValue, binaryFunctionR(*firstL, *firstR));
    }

    return initValue;
}

template <typename InputIterator, typename OutputIterator>
OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator dest) {
    if (first == last) {
        return dest;
    }

    auto total = *first;
    *dest++ = total;

    while (++first != last) {
        total = total + *first;
        *dest++ = total;
    }

    return dest;
}

template <typename InputIterator, typename OutputIterator, typename BinaryFunction>
OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator dest, BinaryFunction binaryFunction) {
    if (first == last) {
        return dest;
    }

    auto total = *first;
    *dest++ = total;

    while (++first != last) {
        total = binaryFunction(total, *first);
        *dest++ = total;
    }

    return dest;
}

template <typename InputIterator, typename OutputIterator>
OutputIterator adjacent_difference(InputIterator first, InputIterator last, OutputIterator dest) {
    if (first == last) {
        return dest;
    }

    auto prev = *first;
    *dest++ = prev;

    while (++first != last) {
        auto curr = *first;
        *dest++ = curr - prev;
        prev = std::move(curr);
    }

    return dest;
}

template <typename InputIterator, typename OutputIterator, typename BinaryFunction>
OutputIterator adjacent_difference(InputIterator first, InputIterator last, OutputIterator dest, BinaryFunction binaryFunction) {
    if (first == last) {
        return dest;
    }

    auto prev = *first;
    *dest++ = prev;

    while (++first != last) {
        auto curr = *first;
        *dest++ = binaryFunction(curr, prev);
        prev = std::move(curr);
    }

    return dest;
}

}

# endif